﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

using Zarion_Quest.Base;

namespace Zarion_Quest.ShotTypes
{   
    /// <summary>
    /// Classe que cria um tiro que irá perseguir um alvo durante um certo tempo. 
    /// </summary>
    class SeekerShot : BaseShot
    {   
        private TimeSpan timerVidaTiro;
        private Object2D currentTarget;
        private Vector2 rotationOrigin;
		
		/// <summary>
		/// Construtor...
        /// </summary>
        /// <param name="texture">Imagem ou sprite sheet do tiro</param>
        /// <param name="columns">Quantidade de colunas que o sprite sheet possui</param>
        /// <param name="rows">Quantidade de linhas que o sprite sheet possui</param>
        /// <param name="position">Posição inicial do tiro</param>
        /// <param name="targets">Lista de alvos que o tiro deve testar colisão</param>
        /// <param name="speed">Velocidade do tiro</param>
        /// <param name="damage">Dano que o tiro inflinge</param>
        /// <param name="shotSoundLocation">Localização do som do tiro, para ser usado no Content.Load</param>
        /// <param name="hitSoundLocation">Localização do som do tiro, para ser usado no Content.Load</param>
		/// <param name="lifeTime">Tempo de vida do tiro em segundos, após esse tempo ele desaparece</param>
        public SeekerShot(Texture2D texture, int columns, int rows, Vector2 position, List<Object2D> targets, int speed, int damage, String shotSoundLocation, String hitSoundLocation, int lifeTime)
            : base(texture, columns, rows, position, targets, speed, damage, shotSoundLocation, hitSoundLocation)
        {
            // atribuo o tempo de vida do tiro em segundos
			timerVidaTiro = new TimeSpan(0, 0, lifeTime);
		}

        /// <summary>
        /// Método que busca pelo alvo mais próximo, e trava o tiro nele...
        /// Se não houver alvos ou o alvo for destruido, ele coloca o tiro em modo de busca.
        /// </summary>
        private void seekNearestTarget()
        {
            if (Targets.Count > 0)
            {
                float currentDistance, smallestDistance = float.MaxValue;
                foreach (Object2D target in Targets)
                {
                    currentDistance = Vector2.Distance(this.Position, target.Position);
                    if (currentDistance < smallestDistance)
                    {
                        smallestDistance = currentDistance;
                        currentTarget = target;
                    }
                }
            }
            else
                currentTarget = null;
        }
		
		/// <summary>
        /// Método para mover na direção do alvo pelo eixo Y.
		/// </summary>
		private void followTargetYAxis()
        {
            if (Position.Y - Speed >= currentTarget.Position.Y)
                Position = new Vector2(Position.X, Position.Y - Speed);
            else if (Position.Y + Speed <= currentTarget.Position.Y)
                Position = new Vector2(Position.X, Position.Y + Speed);
		}
		
		/// <summary>
        /// Método para mover na direção do alvo pelo eixo X.
		/// </summary>
		private void followTargetXAxis()
        {
            if (Position.X - Speed >= currentTarget.Position.X)
                Position = new Vector2(Position.X - Speed, Position.Y);
            else if (Position.X + Speed <= currentTarget.Position.X)
				Position = new Vector2(Position.X + Speed, Position.Y);
		}

        /// <summary>
        /// Rotaciona em torno de um ponto
        /// </summary>
        public void seekerMode()
        {
            Vector2 diffVect = Position - rotationOrigin;

            Vector2 rotatedVect = Vector2.Transform(diffVect, Matrix.CreateFromAxisAngle(new Vector3(0, 0, 1), 0.1f));

            rotatedVect += rotationOrigin;

            Position = rotatedVect;
        }
		
		/// <summary>
        /// Método para mover seguindo o alvo.
		/// </summary>
		/// <param name="gameTime">Game Time</param>
		public override void Update(GameTime gameTime)
        {
            seekNearestTarget();
            if (currentTarget != null)
            {
                followTargetYAxis();
                followTargetXAxis();
            }
            else
            {
                if (rotationOrigin == Vector2.Zero)
                    rotationOrigin = Position - new Vector2(10, 0);
                seekerMode();
            }
            // TODO Fazer o objeto rotacionar para ficar melhor, ou usar um tiro redondo para não ficar feio...

            // Deve ter um jeito menos idiota de se fazer isso, mas ta funcionando...
            timerVidaTiro = timerVidaTiro.Subtract(gameTime.ElapsedGameTime);
            if (timerVidaTiro.CompareTo(TimeSpan.Zero) <= 0)
                IsLive = false;
            base.Update(gameTime);
		}
    }
}